Skip to content

editor: warn when a floor item blocks a door or can't fit its room - #1013

Open
AxiomeCG wants to merge 36 commits into
pascalorg:mainfrom
AxiomeCG:slice/s1b-floor-item-fit
Open

AxiomeCG wants to merge 36 commits into
pascalorg:mainfrom
AxiomeCG:slice/s1b-floor-item-fit

Conversation

@AxiomeCG

@AxiomeCG AxiomeCG commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer guide: a floor item in a door's way is warned by hand too

Stacked on #1007 (the shared tools foundation). This PR targets main, so GitHub lists #1007's commits as well. Review only the top commit: the comparison against #1007. The foundation and this follow-up merge together.

Merge order (in pairs, each editor PR then its private PR): #1007, then pascalorg/private-editor#843, then this PR, then pascalorg/private-editor#852, which points its submodule here.

Why: with #1007, place_items refuses a floor item standing in the space a door needs (blocks_door), or one its room can't hold in any turn (too_large_for_room). The editor's item tool checked only collisions, so a person could do by hand what an agent is refused, with nothing said. This PR is the manual counterpart: one check in core, read by both.

What changes for a user:

  • By hand: while a floor item is placed from the catalog or moved, the HUD's "!" row warns when it stands in a door's way ("Blocks the door to Bath") or its room can't hold it in any turn ("Too large for Bath"). It's a warning, not a block: the item still places. Once it's placed, the selected item's panel shows the same warning at the top, with the item's and the room's sizes when the room is too small.
  • Agents: place_items refuses the same cases with the same words as in core, mcp: the shared tools foundation, one definition per agent tool #1007. The check now lives in core (floorItemFit), and the editor reads the same function.
  • Floor items only: an item on a table, a shelf or a wall isn't checked. Overlapping items aren't a misfit either: a chair under its table is fine.

How to test:

  1. bun install && bun run ci on this branch.
  2. Run the standalone editor (bun dev). Draw a room about 3.1 × 2.2 m, name it Bath, and put a door in one wall.
  3. From the catalog, take the bathtub (2.34 m long) and move it across the door before placing it. The HUD's "!" row reads "Blocks the door to Bath". Place it anyway: it places. Select it: the panel's top shows the warning.
  4. Move the tub clear of the door: the warning goes. Draw a second room of about 2.2 × 1.9 m named Bath 2 and move the tub into it: "Too large for Bath 2", and the panel adds both sizes.
  5. A sink that fits shows nothing. Neither does an item set on a table.
  6. Over the MCP: place_items with the tub across the door is refused blocks_door, naming the door and a spot that clears it.

What to read:

  • packages/core/src/building/floor-item-fit.ts: floorItemFit, the check, and floorItemWarning, the sentence a person reads;
  • packages/editor/src/hooks/use-floor-item-warning.ts: the warning read at the item's live pose while it's placed or moved.

What to skim:

  • packages/core/src/agent-operations/place-items.ts: now calls floorItemFit, with its refusal words unchanged;
  • helper-manager.tsx, item-helper.tsx, registered-tool-helper.tsx and contextual-helper-panel.tsx: the "!" row (notice), placed after main's fence-placement card;
  • packages/nodes/src/item/panel.tsx: the warning at the panel's top.

What to ignore: floor-item-fit.test.ts.


What does this PR do?

The manual counterpart of place_items's door-clearance and room-size refusals:

  • floorItemFit (core, building/) decides whether a floor item fits where it stands: one its room can't hold in any turn, or one in a door's keepout as verify_scene's blocked-door check sees it, doesn't fit. For a door, it suggests a spot in the room that clears every door.
  • place_items refuses with it, and its words are unchanged.
  • floorItemWarning turns a misfit into the sentence a person reads. The editor shows it in the HUD's "!" row while the item is placed or moved, and at the top of the selected item's panel.

No schema change; old scenes load as before.

How to test

  1. bun install && bun run ci.
  2. bun test packages/core/src/building/floor-item-fit.test.ts packages/core/src/agent-operations: the editor and place_items agree on the same spot; an item that fits gets no warning; an item on a table or a wall isn't treated as on the floor; a room with no name still reads as a sentence.
  3. By hand, steps 2–5 of the guide above.

Screenshots / screen recording

Not added yet.

Checklist

  • I've tested this locally with bun dev
  • My code follows the existing code style (run bun check to verify)
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

🤖 Generated with Claude Code


Note

Medium Risk
Large expansion of agent-facing scene mutation and validation paths; regressions would affect MCP/chat parity and what agents can place or refuse, though much is covered by exhaustive fixture-driven tests.

Overview
Expands the shared agent-tool contract so core, MCP, and chat run the same declarative case tables: new fixture modules for add_object, create_room, furnish_room, place_items, search_assets, structure tools (add_wall, add_level, create_stair), room transforms, and find_by_type, plus richer expectations (catalog context, custom check callbacks, surface-specific cases).

Behavior wired in core includes unified operations for rooms (create_room with edge-based doors/windows and reconcile-derived slab/ceiling ids), furnishing and catalog search, walls/levels/stairs (including owned floor openings and empty-scene add_level bootstrap), and add_object rules: required reason on create, refusal of plain wall/slab boxes, and hints when names overlap Pascal-built types.

Scene plumbing gets store-aligned applySceneChanges, mergeSceneChanges for batched undo, applyAgentOutcome (apply + reconcile in tests), achievedChanges for honest delete deltas on MCP/chat, truncated duplicate_level node id payloads, stricter find_by_type / honestNodePatch / requireMaterialRef, verify_scene updates for stair-owned openings and authored-object listing, and optional scene measure/checkpoint registration.

Reviewed by Cursor Bugbot for commit 986e784. Bugbot is set up for automated code reviews on this repo. Configure here.

AxiomeCG and others added 30 commits October 6, 2026 17:19
…e scene store

divide_zone over the hosted MCP crashed with "Cannot read properties of
undefined (reading 'getState')" (Hawkesbury run 2): the store's history
step wrapped a host's runtime too, and the hosted server's store has no
history. Main now applies a host's runtime without that step; this case
keeps it so.

Slice: S1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- add_wall, add_level, create_stair, place_items, create_room, furnish_room,
  search_assets and the fourteen room and floor tools (divide_zone,
  merge_zones, set_floor_foundation, create_mezzanine…) each have one
  contract and one core operation, registered on the MCP and defined by the
  chat from the same object. create_wall, create_level, place_item,
  create_stair_between_levels and cut_opening leave the MCP: add_wall,
  add_level, place_items, create_stair, add_door and add_window replace them.
  create_roof keeps its own definitions for now.
- An operation that reads construction the host derives (re-derived rooms,
  auto ceilings, floor plates) returns afterReconcile; both hosts run it
  through applyAgentOutcome with their own reconciler, in one undo step.
- A mutating shared tool says what it achieved: what the scene holds after
  the call, against a copy taken before it, so a host that writes its node
  map in place no longer reads every creation as unchanged.
- add_object requires a reason, what the object stands in for, checked before
  its script runs; verify_scene lists authored objects with their reason. A
  plain box with a wall's size, or a floor plate, is refused.
- The MCP takes the host's item library for search_assets, place_items and
  furnish_room; a standalone server keeps the built-in list. A search with
  no match points to add_object.
- No listed input schema holds a tuple, so Claude Code keeps every tool (it
  had dropped nine room and floor tools), and shared tools declare loose
  outputs, so a field core adds no longer fails a client that listed them.

Slice: S1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d opening)

A cased opening or an arched passage, as the editor's door panel offers:
add_door takes openingKind 'opening' and builds a door node with no leaf,
framed or arched by its outline.

Slice: S1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in its way

A build that deletes plan-only floors, then copies a floor up, pushed every
floor above with each copy, gap included, so a model spent calls on
reorder_level. A copy now fills the free floor; without a gap nothing
changes, in the editor's Duplicate too.

Slice: S1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 3 (2026-10-05) wrote its front door's three glass strips off as "not
possible": both tools take code, a script for what the fields cannot
express, and their descriptions stopped at outline, type and style. They
now say that glass strips, a pattern of lites or carved panels are written
as a script in code, never left as not possible.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 923f64005ae8abc2a05977ed34ead326eec2d0c3)
Working parity, from the MCP ↔ chat runner's first report:
- add_door and add_window are one core operation, addWallOpening, and
  one result the contract declares: ok, the id, wallId, localX, t,
  wallLength, clamped, coordinateSystem, sillHeight, message and
  achieved. The MCP's `position` copy of t is gone; the input keeps it.
  A rebuilt opening answers the same, rebuiltOpeningResult.
- find_by_type refuses a level that is not there (level_not_found), as
  get_zones does; it answered "nothing found".
- add_level on a scene with no building starts it as the editor does: a
  site (unless one stands), a building, its ground level. An agent that
  cleared the scene could not begin again with the tools.
- create_project saves the session's scene as the project's first
  draft: from any other session it was scene_not_found until a save.
- Every write says where it went, top-level: project, and `unsaved` with
  how to keep it on a scratch scene no project holds (writeTarget). A
  result's own `note` stays the operation's.
Shared-table cases for find_by_type and the fresh start; the MCP's
tests move from `position` to t and achieved.

Slice: S1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 3 took five attempts to paint a pier grey through patches:
`material: {color}` reported applied and stored {} (the schemas drop
unknown keys), `materialPreset: null` was refused (no way to clear), the
preset hid the material set under it, and an unknown preset rendered grey
with no word.

honestNodePatch, on apply_patch's updates: a path the node would drop or
change is refused naming it (unknown_field: material.color, where a colour
goes in material.properties.color), a material the library lacks is refused
naming the nearest of the target's kind (unknown_material), a material a
set preset hides is refused naming the preset (shadowed_field), and null
clears a field (field_required when the field is required). Free-form
records (metadata, slots) pass as sent.

Slice: S1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
S1's rename of place_item into place_items placed on a level's floor
only. As main's place_item did, an item may now name its host
(targetNodeId), its position in level coordinates as main took it:

- a wall: along it from its start, y the height of the item's bottom
  (required), on the side of the wall the point is on (the front faces
  its left; the back is turned round), centred in the wall or, for a
  wall-side fixture, on its face; kept inside the span and the height,
  as the editor's wall placement keeps it; wallId, wallT and side set;
- a ceiling: under it, its top flush;
- an item standing on a floor: in its frame. An object built with
  add_object takes it on its real surface under the point, or hangs a
  ceiling item from its underside above it (a sloped vault), and the
  result names the part (restingOn); a catalog item, on its top;
- a room, a slab or a level: its floor.

Each item is refused on its own: height_required, item_too_tall,
unsupported_host (a door), host_not_found, host_not_on_level (an item
on an item). An id the library lacks stays refused (asset_not_found)
rather than placed as main's 0.5 m placeholder. The surface logic is
the chat's place_on_surface's, moved into core.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tions

S1's create_stair dropped what main's create_stair_between_levels set:

- railingMode (none, left, right, both; default both);
- materialPreset, on the flight and its segment, refused when the
  library lacks it, naming the flooring it has; a stair takes no paint,
  so the colour it points to is a flat library one (library:preset-*).
  The lab's requireMaterialRef checks it, as apply_patch and
  update_node do: upstream's unknownMaterialPresetRefusal rides on its
  node-slots module, which the lab does not have yet; the two meet when
  the lab merges upstream;
- name (default Staircase N);
- the opening controls, under main's names: createDestinationSlabOpening,
  createSourceCeilingOpening, destinationSlabId, sourceCeilingId,
  openingWidth, openingLength, openingOffset, openingCenter and
  openingRotation (in degrees, as the flight's rotation).

With none of them, the stair cuts its own opening as the editor's stair
tool does (openingOffset is its margin, 0.08 m by default; both cuts off
cuts none). With any, the opening is cut as given, as main cuts it: a
floor opening owned by the stair, with the pose the live opening systems
move it by, and the stair's own opening off so nothing is cut twice.
Main centred that opening at z + length / 2 whatever the flight's turn;
here it follows the climb, so a turned flight's opening stays over it.

slab_not_found and ceiling_not_found refuse ids that name no slab or
ceiling.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…read alike

A Zod tuple renders as prefixItems in the chat's JSON schema and as a
flattened array on the MCP, so agent-surface parity found the two input
schemas apart. openingCenter is a length-2 number array, as every other
contract writes an (x, z) point.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The pascal-3d workflows said place_items places on a level's floor only;
it now takes a host per item (targetNodeId), as main's place_item did.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… default

requireMaterialRef names a flat library colour (library:preset-*) unless
its caller asks for paint, as paint does: honest patches and create_stair
then point to no tool a slice may lack, and create_stair needs no flag.

Slice: S1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… it empty

2026-10-03, a Claude Code run on the hosted MCP: create_project in a
session that had another project loaded returned that project's levels,
and the next save would have written it into the new project. The
session is now bound to the new project first, then, when it was bound
to another one, its scene is emptied to the default site, building and
level, with no undo back to the old one. Work not bound to any project
is kept: it is what the new project is for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… was not loaded from

2026-10-03, a Claude Code run on the hosted MCP: the server reloaded, the
session started over on a blank scene, and save_scene(projectId) wrote it
over the project's draft (8 levels, 10 imported plans, the calibration
and the alignment, no history). save_scene now refuses scene_not_loaded
when the session's scene was not loaded from (or created for) the target
project or scene and that one holds more than the default site, building
and level; replace: true writes over it on purpose. Live sync already
writes only to the scene a session loaded.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Run 4's first save_scene, right after create_project bound the session to its
project, was refused "projectId is required for Supabase store. Call
create_project first." With no id and no projectId, save_scene now saves the
current scene to the scene this session is bound to (create_project,
load_scene or an earlier save); with nothing bound, it saves as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A floor copy is hundreds of ids the model never uses; written into the
prompt cache once, they are read back on every call after. The result
keeps the counts per type in `copied`, lists the first 40 ids and says how
many it left out (`newNodeIdsOmitted`), which the MCP declares.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since owned floor openings, a stair's opening is a floor-opening node on
the floor above, owned by the stair. The check already looks for it; these
cases pin it: an owned opening on the floor above counts, one drawn on the
stair's own ceiling does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every write gives an `achieved` (what the scene holds after the call), but
the MCP's add_object returned none; the chat's add_object did. The headless
parity gate could not see it, its chat lane unreachable on that row; parity
with an editor tab showed it. The MCP now counts the change against a copy of
the scene before the call, as its shared tools and the chat do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 4 (L66): the agent made the porch an outdoor room, so the house wall
behind it had a room on both faces and knew no outside. The front door faced
the hall, view_scene showed its outside from inside, and the porch face took
the inside finish.

A room is outdoor when it has no ceiling and can be left without crossing a
wall, through a separator no room stands behind or into another such room,
found as a fixed point. A wall face on an outdoor room is exterior, so door
facing, view_scene, facades and finishes follow on both paths. A room that
only lost its ceiling, a kitchen open to a terrace, and a courtyard walled on
every side stay inside; a carport (no ceiling, an open side) is outside.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ywhere

L59 replaced "blocks door clearance" with the item, its size and the door it
is in the way of; the MCP test and the clearance log still read the old text,
and the old reason entry was left unused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r too large for its room

L59, run 4: furnish_room skipped the catalog's 2.34 m bathtub as blocking
the bath's door; the agent then set it there itself with place_items,
which checked only that its centre stood in a room, and verify_scene
ended on two blocked doors.

A floor item is now refused on its own when the room it stands in holds
it in no turn (too_large_for_room, with both sizes, pointing to a
smaller one or add_object at the room's size), or when it stands in the
space a door needs, as verify_scene's blocked-door check sees it
(blocks_door, naming the door and a spot in the room that clears every
door, when there is one). Overlaps are not refused: a chair under its
table and a bed on its rug overlap by design. Items on a host keep
their own checks.

furnish_room's skips name the item's size, the door in the way, or
that the room is too small and what instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tant (L68)

The store's new-scene level wrote a literal 2.5. It now writes
DEFAULT_LEVEL_HEIGHT, as the hosted MCP's default scene and a new
project's empty scene do, so the three cannot drift apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test asked for one roofing material by its id's prefix, which is one
library's naming, not every library's. It now asserts roofing first as the
library tags it (surfaces include roof), which every library with roofing
passes; which roofing that is stays the data's business.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s what they protect

apply_patch held the honest-update check inline. A patch guard now
registers from its own module, run by order; apply_patch runs what is
registered and passes an McpError through as it is. The honest-update check
is the first, from honest-patch-guard.ts, and answers as it did:
{ code, patchIndex, id, message }.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A door's swing and a sectional door's track run behind its facing; add_door
always faced the wall's front, so a garage drawn with its front inside ran
its overhead track on the street, and a front door behind a porch faced the
hall. add_door now faces a door out when the wall knows its outside
(resolveWallExteriorSide): rotation π and side 'back' when the outside is
the back. Walls that do not know their outside, and inside walls, keep the
front as before. One rule for agents and people: the editor's door tool
places a door on an outside wall facing out whichever face is hovered
(placedDoorFace); R still flips it.

The shared opening cases pin both wall directions and the inside wall.
Doors made in the same call as their walls are not covered: their walls
learn their sides only afterwards.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The outdoor-sides cases came with checks of a facade fill and a view's
framing from the porch, tools this branch does not have. The porch case now
checks what it owns: the wall is exterior on the porch face, and add_door
places a door that faces the porch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ir winders) into the shared tools foundation

Where main and this branch did the same thing, main's version stands:
- measure_stair and fit_stair (pascalorg#1000) are shared tools next to create_stair:
  one stairs contract module and both operations registered.
- verify_scene keeps main's informational issues (hasIssues ignores
  severity 'info') with this branch's authored objects and checks.
- create_stair_between_levels stays replaced by create_stair; main's
  sizing of a new flight from the design targets (planStairCreation)
  is carried into create_stair in the next commit.
- The tool lists (annotations, README, the skill) gain main's two tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main sizes a new flight from the stair's design targets and resolves its
rise against what it stands on and arrives at (planStairCreation, pascalorg#1000).
create_stair now builds its flight the same way: the run and the risers come
from the design targets unless given, and the stair keeps uniform risers and
its flight's step count. main's two sizing cases now run on create_stair.

The ring of a stair's owned opening may start at another corner once main
re-plans it after a move; the case compares the ring's extent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…into the foundation

Both sides kept: the foundation's furnish_room export and asset catalog, main's
optional hosted service tools, registered before the resources.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pascal

pascal Bot commented Oct 6, 2026

Copy link
Copy Markdown

This is the by-hand counterpart to a refusal agents already get. Since #1007, place_items turns down a floor item standing in the space a door needs, or one its room can't hold in any turn, while the editor's item tool checked only collisions, so a person could place by hand what an agent was refused and hear nothing about it. The check moves into core as floorItemFit, place_items now calls it with its refusal wording unchanged, and the editor reads the same function: the HUD's "!" row while an item is placed or moved, plus a row at the top of the selected item's panel. It warns rather than blocks, and it only looks at floor items.

The branch is stacked on #1007 and targets main, so GitHub lists 136 files. The work this PR adds is the top two commits, 13 files:

File Change What changed
packages/core/src/building/floor-item-fit.ts added floorItemFit (room bounds in either turn, door keepouts, and a spot that clears every door) and floorItemWarning, the sentence a person reads
packages/core/src/building/floor-item-fit.test.ts added cases for the check and its wording
packages/core/src/building/index.ts modified exports the new module
packages/core/src/agent-operations/place-items.ts modified its inline fit logic replaced by a floorItemFit call; both refusal messages kept word for word
packages/core/src/agent-operations/furnish-room.ts modified reworded a comment
packages/core/src/agent-operations/__fixtures__/place-items-cases.ts modified reworded a two-line comment
packages/editor/src/hooks/use-floor-item-warning.ts added useFloorItemWarning runs the check at the item's live pose; placingItemId finds the transient draft being placed
packages/editor/src/index.tsx modified exports the hook
packages/editor/src/components/ui/helpers/helper-manager.tsx modified computes the warning for the moving or placing item and passes it to both helpers
packages/editor/src/components/ui/helpers/item-helper.tsx modified takes a notice prop and forwards it
packages/editor/src/components/ui/helpers/registered-tool-helper.tsx modified forwards notice, and no longer returns null when a notice is the only content
packages/editor/src/components/ui/helpers/contextual-helper-panel.tsx modified renders notice as the "!" hint row
packages/nodes/src/item/panel.tsx modified amber warning block at the top of the selected item's panel
123 other files from #1007 the shared tools foundation this branch stacks on, shown because both target main

Start with floor-item-fit.ts, then place-items.ts to confirm the agent path only changed where the check lives, then use-floor-item-warning.ts and panel.tsx for what a person sees. The author's range link skipping the foundation is in the description.

AxiomeCG and others added 2 commits October 6, 2026 22:11
… labels it, a Separator

Divide, merge, delete and an outdoor room's outline now say "a separator (the
editor's Separator: a room boundary with no wall)", so an agent describes what
the person finds in the Scene panel instead of an "open divider", the door and
window panels' word for the bars between panes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment-only: each comment keeps the failure that motivated the code and drops
which session found it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AxiomeCG
AxiomeCG force-pushed the slice/s1b-floor-item-fit branch from 9b3f92c to e1b9464 Compare October 6, 2026 20:17
AxiomeCG and others added 4 commits October 6, 2026 22:40
An object named after something Pascal builds is built with a hint naming the
tool for it. Four of those hints named tools this server does not register
(create_reference_elements, create_stairs_and_lifts, correct_plan_reading,
apply_facade), so an agent following one called a tool that fails. They now
name add_wall, create_room with its floor tools, add_door / add_window,
create_stair and create_roof; lifts and balconies, which have no tool here,
get no hint. A test checks every tool a hint names against tools/list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… falls in

With opening controls, create_stair reported the storey's first slab and first
ceiling as the surfaces it cut, and took any slab upstairs as proof a hole was
cut there. On a floor of several rooms that named the wrong room's slab, and an
opening under no slab was still reported cut. It now picks the slab above and
the ceiling below that hold the opening's centre; a slab or ceiling id given
still wins. main's create_stair_between_levels picked the first surface the
same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… above is cut

slabHoleCut counted every opening the flight cut, so a stair cutting only the
ceiling below reported a hole in the slab above, and an agent took the upper
floor for open when it was not. It is now true only for an opening drawn on
the floor above, as the chat's create_stair meant it on main ("auto floor
opening").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is warned while placed

place_items refuses a floor item standing in the space a door
needs, or one its room cannot hold in any turn, while the editor's item tool
checked only collisions. The check moves into core (floorItemFit), place_items
keeps its refusal words, and the editor reads the same function for a person:
a warning, not a block, in the HUD's "!" row while the item is placed or
moved ("Blocks the door to Bath", "Too large for Bath"), and at the top of the
selected item's panel with the sizes. Floor items only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AxiomeCG
AxiomeCG force-pushed the slice/s1b-floor-item-fit branch from e1b9464 to 986e784 Compare October 6, 2026 21:29
@AxiomeCG
AxiomeCG marked this pull request as ready for review October 7, 2026 12:54
AxiomeCG added a commit to AxiomeCG/editor that referenced this pull request Oct 7, 2026
… warning (pascalorg#1013) into the sync bundle

Union with main's clear_scene (pascalorg#1012): its contract and registration sit beside
the foundation's tools; live sync keeps the foundation's { status, project }
answer and main's deliberate wipe path; apply_patch answers a store's refusal
with its code, then rethrows MCP errors; the annotation count is computed from
the policy, which lists clear_scene.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant